package org.romzzz.gwtclassifieds.client.ui;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.romzzz.gwtclassifieds.client.data.CategoryBean;
import org.romzzz.gwtclassifieds.client.data.ClassifiedBean;
import org.romzzz.gwtclassifieds.client.i18n.LocalizedStrings;
import org.romzzz.gwtclassifieds.client.service.ClassifiedService;
import com.extjs.gxt.ui.client.Style;
import com.extjs.gxt.ui.client.event.ButtonEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.GroupingStore;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.LabelField;
import com.extjs.gxt.ui.client.widget.form.StoreFilterField;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnData;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
import com.extjs.gxt.ui.client.widget.grid.GridGroupRenderer;
import com.extjs.gxt.ui.client.widget.grid.GroupColumnData;
import com.extjs.gxt.ui.client.widget.grid.GroupingView;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.toolbar.FillToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * This is the classified grid, containing all the classified that correspond to the left panel (categories) selection.
 * It uses grouping and sorting, and columns can be shown / hidden using header menus.
 */
public class ClassifiedGrid extends ContentPanel {
	
	GroupingStore<ClassifiedBean> store;
	private ClassifiedGrid dis = this;
	private CategoryList categoryList;
	private ClassifiedDetails classifiedDetails;
	
	private LocalizedStrings localizedStrings = GWT.create(LocalizedStrings.class);

	public ClassifiedGrid() {
		super(new FitLayout());
		setHeaderVisible(false);
		store = new GroupingStore<ClassifiedBean>();
		store.groupBy("date");
		final ColumnModel cm = getColumnModel();
		GroupingView view = new GroupingView();
		view.setGroupRenderer(new GridGroupRenderer() {
			public String render(GroupColumnData data) {
				String f = cm.getColumnById(data.field).getHeader();
				String l = data.models.size() < 2 ? localizedStrings.classified() : localizedStrings.classifieds();
				String disp ;
				if (data.field.equals("offer")) {
					disp = data.group.equals("true") ? localizedStrings.offer() : localizedStrings.seek();
				} else if (data.field.equals("date")) {
					disp = DateTimeFormat.getFormat("d.M.y").format((Date) data.gvalue);
				} else {
					disp = data.group;
				}
				return f + ": " + disp + " (" + data.models.size() + " " + l + ")";
			}
		});
		
		final Grid<ClassifiedBean> grid = new Grid<ClassifiedBean>(store, cm);
		grid.setView(view);
		view.setAutoFill(true);
		grid.setAutoWidth(true);
		grid.getSelectionModel().setSelectionMode(Style.SelectionMode.SINGLE);
		add(grid);
		
		final Button addClassified = new Button(localizedStrings.addClassified());
		addClassified.addSelectionListener(new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				new ClassifiedPopup(null, null, categoryList.getAllCategories(), dis);
			}
		});
		final Button editClassified = new Button(localizedStrings.editClassified());
		editClassified.addSelectionListener(new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				new PasswordPrompt(localizedStrings.enterPassword()) {
					@Override
					public void onOK(final String value) {
						final ClassifiedBean beanToUpdate = grid.getSelectionModel().getSelectedItem();
						if (beanToUpdate != null && beanToUpdate.getId() > 0) {
							final MessageBox box = MessageBox.wait(localizedStrings.pleaseWait(), null, localizedStrings.validation());
							ClassifiedService.App.getInstance().checkAcces(beanToUpdate.getId(), value, new AsyncCallback<Long>() {
								public void onFailure(Throwable caught) {
									box.close();
									MessageBox.alert(localizedStrings.error(), caught.toString(), null);
								}
								public void onSuccess(Long result) {
									box.close();
									new ClassifiedPopup(beanToUpdate, result, categoryList.getAllCategories(), dis);
								}
							});
							
						}
					}
				}.show();
			}
		});
		final Button removeClassified = new Button(localizedStrings.removeClassified());
		removeClassified.addSelectionListener(new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				new PasswordPrompt(localizedStrings.enterPassword()) {
					@Override
					public void onOK(String value) {
						final ClassifiedBean beanToRemove = grid.getSelectionModel().getSelectedItem();
						if (beanToRemove != null && beanToRemove.getId() > 0) {
							final MessageBox box = MessageBox.wait(localizedStrings.pleaseWait(), null, localizedStrings.validation());
							ClassifiedService.App.getInstance().removeClassified(beanToRemove.getId(), value, categoryList.getCheckedCategories(), new AsyncCallback<List<ClassifiedBean>>() {
								public void onFailure(Throwable caught) {
									box.close();
									MessageBox.alert(localizedStrings.error(), caught.toString(), null);
								}
								public void onSuccess(List<ClassifiedBean> result) {
									box.close();
									init(result);
								}
							});
						}
					}
				}.show();
			}
		});
		final LabelField filterLabel = new LabelField(localizedStrings.filter());
		filterLabel.addStyleName("filterLabel");
		final StoreFilterField<ClassifiedBean> filterClassifieds = new StoreFilterField<ClassifiedBean>() {
			@Override
			protected boolean doSelect(Store<ClassifiedBean> store, ClassifiedBean parent, ClassifiedBean record, String property, String filter) {
				return isContainedInTheseIgnoreCase(filter, record.getTitle(), record.getBody(), record.getCategory().getValue(), record.getContact());
			}
		};
		filterClassifieds.bind(store);
		final Button feedback = new Button(localizedStrings.feedback());
		feedback.addSelectionListener(new SelectionListener<ButtonEvent>() {
			@Override
			public void componentSelected(ButtonEvent ce) {
				new FeedbackPrompt().show();
			}
		});
		
		
		ToolBar buttonBar = new ToolBar();
		buttonBar.add(addClassified);
		buttonBar.add(editClassified);
		buttonBar.add(removeClassified);
		buttonBar.add(filterLabel);
		buttonBar.add(filterClassifieds);
		buttonBar.add(new FillToolItem());
		buttonBar.add(feedback);
		setTopComponent(buttonBar);
		editClassified.setEnabled(false);
		removeClassified.setEnabled(false);
		grid.getSelectionModel().addSelectionChangedListener(new SelectionChangedListener<ClassifiedBean>() {
			@Override
			public void selectionChanged(SelectionChangedEvent<ClassifiedBean> se) {
				ClassifiedBean selection = se.getSelectedItem();
				if (selection != null) {
					editClassified.setEnabled(true);
					removeClassified.setEnabled(true);
					classifiedDetails.setContent(selection);
				} else {
					editClassified.setEnabled(false);
					removeClassified.setEnabled(false);
					classifiedDetails.clear();
				}
			}
		});
	}
	
	private ColumnModel getColumnModel() {
		List<ColumnConfig> columns = new ArrayList<ColumnConfig>();
		ColumnConfig config = new ColumnConfig("offer", localizedStrings.type(), 45);
		config.setRenderer(new GridCellRenderer<ClassifiedBean>() {
			@Override
			public Object render(ClassifiedBean bean, String property, ColumnData config, int rowIndex, int colIndex, ListStore<ClassifiedBean> store, Grid<ClassifiedBean> grid) {
				return bean.isOffer() ? localizedStrings.offer() : localizedStrings.seek();
			}
		});
		columns.add(config);
		columns.add(new ColumnConfig("title", localizedStrings.title(), 150));
		config = new ColumnConfig("date", localizedStrings.date(), 80);
		config.setDateTimeFormat(DateTimeFormat.getFormat("d.M.y"));
		columns.add(config);
		columns.add(new ColumnConfig("body", localizedStrings.content(), 320));
		columns.add(new ColumnConfig("category", localizedStrings.category(), 90));
		return new ColumnModel(columns);
	}
	
	private void init(List<ClassifiedBean> classifieds) {
		store.removeAll();
		store.add(classifieds);
		classifiedDetails.clear();
	}
	
	/**
	 * Refresh the classified grid using RPC to retrieve all the classified that match the selected categories.
	 */
	public void refresh() {
		final MessageBox box = MessageBox.wait(localizedStrings.pleaseWait(), null, localizedStrings.loadingClassifieds());
		ClassifiedService.App.getInstance().getCategorizedClassifieds(categoryList.getCheckedCategories(), new AsyncCallback<List<ClassifiedBean>>() {
			public void onFailure(Throwable caught) {
				box.close();
				MessageBox.alert(localizedStrings.error(), caught.toString(), null);
			}
			public void onSuccess(List<ClassifiedBean> classifieds) {
				box.close();
				init(classifieds);
			}
		});
	}
	
	public List<CategoryBean> getCheckedCategories() {
		return categoryList.getCheckedCategories();
	}
	
	public void setCategoryList(CategoryList list) {
		this.categoryList = list;
	}
	
	/**
	 * This method is used to add a reference to the classified details panel that will have to be updated depending on the selected classified in the grid.
	 * 
	 * @param details
	 */
	public void setClassifiedDetails(ClassifiedDetails details) {
		this.classifiedDetails = details;
	}
	
	private boolean isContainedInTheseIgnoreCase(String token, String ... strings) {
		String lowerCaseToken = token.toLowerCase();
		for (int i=0; i<strings.length; i++) {
			if (strings[i].toLowerCase().contains(lowerCaseToken)) {
				return true;
			}
		}
		return false;
	}
}
